#include "Utils.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <ctime>

namespace Utils {

std::string GetCurrentDateFolder() {
    time_t now = time(0);
    struct tm timeinfo;
    localtime_s(&timeinfo, &now);

    std::ostringstream oss;
    oss << std::setfill('0')
        << std::setw(4) << (timeinfo.tm_year + 1900)
        << std::setw(2) << (timeinfo.tm_mon + 1)
        << std::setw(2) << timeinfo.tm_mday;

    return oss.str();
}

std::string GetCurrentTimeFilename() {
    time_t now = time(0);
    struct tm timeinfo;
    localtime_s(&timeinfo, &now);

    std::ostringstream oss;
    oss << std::setfill('0')
        << std::setw(2) << timeinfo.tm_hour
        << std::setw(2) << timeinfo.tm_min
        << std::setw(2) << timeinfo.tm_sec
        << ".bmp";

    return oss.str();
}

std::string GetFullOutputPath(const std::string& basePath) {
    std::string datePath = GetCurrentDateFolder();
    std::string filename = GetCurrentTimeFilename();

    std::string fullPath = basePath;
    if (!fullPath.empty() && fullPath.back() != '\\' && fullPath.back() != '/') {
        fullPath += "\\";
    }
    fullPath += datePath + "\\" + filename;

    return fullPath;
}

bool IsWindowsVistaOrLater() {
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    if (GetVersionEx(&osvi)) {
        return (osvi.dwMajorVersion >= 6);
    }

    return false;
}

bool IsWindows8OrLater() {
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    if (GetVersionEx(&osvi)) {
        return (osvi.dwMajorVersion > 6) ||
               (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 2);
    }

    return false;
}

std::string GetLastWin32Error() {
    DWORD errorCode = GetLastError();
    if (errorCode == 0) {
        return "No error";
    }

    LPSTR messageBuffer = nullptr;
    size_t size = FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        errorCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPSTR)&messageBuffer,
        0,
        NULL
    );

    std::string message;
    if (size > 0 && messageBuffer) {
        message = std::string(messageBuffer, size);
        while (!message.empty() && (message.back() == '\r' || message.back() == '\n')) {
            message.pop_back();
        }
    } else {
        std::ostringstream oss;
        oss << "Error code: " << errorCode;
        message = oss.str();
    }

    LocalFree(messageBuffer);
    return message;
}

void DebugLog(const std::string& message) {
#ifdef _DEBUG
    std::cout << "[ScreenCapture] " << message << std::endl;
#endif
    OutputDebugStringA(("[ScreenCapture] " + message + "\n").c_str());
}

bool EnableDebugPrivilege() {
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        return false;
    }

    TOKEN_PRIVILEGES tokenPrivileges;
    tokenPrivileges.PrivilegeCount = 1;
    tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tokenPrivileges.Privileges[0].Luid)) {
        CloseHandle(hToken);
        return false;
    }

    bool result = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL) != FALSE;
    CloseHandle(hToken);

    return result;
}

DWORD GetCurrentSessionId() {
    DWORD sessionId = 0;
    if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) {
        return 0;
    }
    return sessionId;
}

}